package com.datagateway.component;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 脚本A/B测试管理器
 * 负责Groovy脚本的A/B测试执行、流量分配和结果分析
 * 
 * @author Data Gateway Team
 * @version 1.0.0
 */
@Component
public class ScriptABTestManager {

    private static final Logger logger = LoggerFactory.getLogger(ScriptABTestManager.class);

    @Autowired
    private ScriptVersionManager scriptVersionManager;

    @Autowired
    private AlertManager alertManager;

    @Autowired
    private SystemMonitor systemMonitor;

    /**
     * A/B测试统计
     */
    private final AtomicLong totalABTestRequests = new AtomicLong(0);
    private final AtomicLong totalVersionARequests = new AtomicLong(0);
    private final AtomicLong totalVersionBRequests = new AtomicLong(0);
    private final AtomicLong totalABTestErrors = new AtomicLong(0);

    /**
     * A/B测试配置
     */
    private boolean abTestEnabled = true;
    private int defaultTestDurationMinutes = 60;
    private double defaultTrafficRatio = 0.5;
    private int maxConcurrentTests = 5;
    private boolean autoStopOnError = true;
    private int errorThreshold = 10;

    /**
     * 活跃的A/B测试
     */
    private final Map<String, ABTestSession> activeTests = new ConcurrentHashMap<>();
    private final Map<String, ABTestResult> testResults = new ConcurrentHashMap<>();

    /**
     * 执行器
     */
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2, r -> {
        Thread t = new Thread(r, "script-ab-test-manager");
        t.setDaemon(true);
        return t;
    });

    /**
     * 初始化脚本A/B测试管理器
     */
    public void initialize() {
        try {
            // 启动A/B测试监控任务
            startABTestMonitoringTask();
            
            logger.info("脚本A/B测试管理器初始化成功");
            
        } catch (Exception e) {
            logger.error("脚本A/B测试管理器初始化失败", e);
            alertManager.sendSystemErrorAlert("脚本A/B测试管理器初始化失败", e.getMessage());
        }
    }

    /**
     * 停止脚本A/B测试管理器
     */
    public void stop() {
        try {
            scheduler.shutdown();
            if (!scheduler.awaitTermination(30, TimeUnit.SECONDS)) {
                scheduler.shutdownNow();
            }
            
            logger.info("脚本A/B测试管理器停止成功");
            
        } catch (Exception e) {
            logger.error("脚本A/B测试管理器停止失败", e);
        }
    }

    /**
     * 开始A/B测试
     * 
     * @param scriptName 脚本名称
     * @param versionA 版本A
     * @param versionB 版本B
     * @param trafficRatio 流量比例
     * @param duration 测试持续时间（分钟）
     * @return 是否开始成功
     */
    public boolean startABTest(String scriptName, String versionA, String versionB, 
                             double trafficRatio, int duration) {
        if (!abTestEnabled) {
            logger.debug("A/B测试已禁用，跳过测试开始: {}", scriptName);
            return false;
        }

        try {
            // 检查是否已有活跃测试
            if (activeTests.containsKey(scriptName)) {
                logger.warn("脚本已有活跃的A/B测试: {}", scriptName);
                return false;
            }

            // 检查最大并发测试数
            if (activeTests.size() >= maxConcurrentTests) {
                logger.warn("已达到最大并发A/B测试数: {}", maxConcurrentTests);
                return false;
            }

            // 验证脚本版本
            ScriptVersionManager.ScriptVersion versionAObj = scriptVersionManager.getScriptVersion(scriptName, versionA);
            ScriptVersionManager.ScriptVersion versionBObj = scriptVersionManager.getScriptVersion(scriptName, versionB);
            
            if (versionAObj == null || versionBObj == null) {
                logger.error("A/B测试脚本版本不存在: {} v{} vs v{}", scriptName, versionA, versionB);
                return false;
            }

            // 创建A/B测试会话
            ABTestSession testSession = new ABTestSession(
                scriptName,
                versionA,
                versionB,
                trafficRatio,
                duration,
                System.currentTimeMillis()
            );

            activeTests.put(scriptName, testSession);
            
            logger.info("A/B测试开始: {} v{} vs v{} (比例: {}, 持续时间: {}分钟)", 
                       scriptName, versionA, versionB, trafficRatio, duration);
            
            return true;
            
        } catch (Exception e) {
            logger.error("开始A/B测试失败: {} v{} vs v{}", scriptName, versionA, versionB, e);
            alertManager.sendSystemErrorAlert("开始A/B测试失败", e.getMessage());
            return false;
        }
    }

    /**
     * 停止A/B测试
     * 
     * @param scriptName 脚本名称
     * @param winningVersion 获胜版本
     * @return 是否停止成功
     */
    public boolean stopABTest(String scriptName, String winningVersion) {
        try {
            ABTestSession testSession = activeTests.remove(scriptName);
            if (testSession == null) {
                logger.warn("A/B测试会话不存在: {}", scriptName);
                return false;
            }

            // 生成测试结果
            ABTestResult testResult = generateTestResult(testSession, winningVersion);
            testResults.put(scriptName, testResult);

            // 部署获胜版本
            if (winningVersion != null) {
                scriptVersionManager.deployScriptVersion(scriptName, winningVersion);
            }

            logger.info("A/B测试停止: {} 获胜版本: {}", scriptName, winningVersion);
            
            return true;
            
        } catch (Exception e) {
            logger.error("停止A/B测试失败: {}", scriptName, e);
            return false;
        }
    }

    /**
     * 选择脚本版本（用于A/B测试）
     * 
     * @param scriptName 脚本名称
     * @param requestId 请求ID
     * @return 选中的脚本版本
     */
    public ScriptVersionSelection selectScriptVersion(String scriptName, String requestId) {
        try {
            ABTestSession testSession = activeTests.get(scriptName);
            if (testSession == null) {
                // 没有A/B测试，返回活跃版本
                ScriptVersionManager.ScriptVersion activeVersion = scriptVersionManager.getActiveScriptVersion(scriptName);
                return new ScriptVersionSelection(activeVersion, "active", false);
            }

            // 执行A/B测试逻辑
            return executeABTestLogic(testSession, requestId);
            
        } catch (Exception e) {
            logger.error("选择脚本版本失败: {}", scriptName, e);
            totalABTestErrors.incrementAndGet();
            
            // 返回活跃版本作为降级
            ScriptVersionManager.ScriptVersion activeVersion = scriptVersionManager.getActiveScriptVersion(scriptName);
            return new ScriptVersionSelection(activeVersion, "fallback", true);
        }
    }

    /**
     * 记录A/B测试结果
     * 
     * @param scriptName 脚本名称
     * @param version 版本
     * @param success 是否成功
     * @param processingTime 处理时间
     * @param errorMessage 错误信息
     */
    public void recordABTestResult(String scriptName, String version, boolean success, 
                                 long processingTime, String errorMessage) {
        try {
            ABTestSession testSession = activeTests.get(scriptName);
            if (testSession == null) {
                return;
            }

            // 更新测试会话统计
            if (version.equals(testSession.getVersionA())) {
                testSession.incrementVersionARequests();
                if (success) {
                    testSession.incrementVersionASuccess();
                } else {
                    testSession.incrementVersionAErrors();
                }
                testSession.addVersionAProcessingTime(processingTime);
            } else if (version.equals(testSession.getVersionB())) {
                testSession.incrementVersionBRequests();
                if (success) {
                    testSession.incrementVersionBSuccess();
                } else {
                    testSession.incrementVersionBErrors();
                }
                testSession.addVersionBProcessingTime(processingTime);
            }

            // 检查错误阈值
            if (autoStopOnError && testSession.getTotalErrors() > errorThreshold) {
                logger.warn("A/B测试错误数超过阈值，自动停止: {} (错误数: {})", 
                           scriptName, testSession.getTotalErrors());
                stopABTest(scriptName, null);
            }
            
        } catch (Exception e) {
            logger.error("记录A/B测试结果失败: {}", scriptName, e);
        }
    }

    /**
     * 获取活跃的A/B测试
     * 
     * @return 活跃测试列表
     */
    public List<ABTestSession> getActiveABTests() {
        return new ArrayList<>(activeTests.values());
    }

    /**
     * 获取A/B测试结果
     * 
     * @param scriptName 脚本名称
     * @return 测试结果
     */
    public ABTestResult getABTestResult(String scriptName) {
        return testResults.get(scriptName);
    }

    /**
     * 获取所有A/B测试结果
     * 
     * @return 测试结果列表
     */
    public List<ABTestResult> getAllABTestResults() {
        return new ArrayList<>(testResults.values());
    }

    /**
     * 获取A/B测试统计信息
     * 
     * @return 统计信息
     */
    public ABTestStatistics getStatistics() {
        return new ABTestStatistics(
            totalABTestRequests.get(),
            totalVersionARequests.get(),
            totalVersionBRequests.get(),
            totalABTestErrors.get(),
            activeTests.size(),
            testResults.size(),
            abTestEnabled,
            defaultTestDurationMinutes,
            defaultTrafficRatio,
            maxConcurrentTests,
            System.currentTimeMillis()
        );
    }

    /**
     * 设置A/B测试配置
     * 
     * @param enabled 是否启用A/B测试
     * @param defaultDuration 默认测试持续时间
     * @param defaultRatio 默认流量比例
     * @param maxConcurrent 最大并发测试数
     * @param autoStop 自动停止
     * @param errorThreshold 错误阈值
     */
    public void setConfiguration(boolean enabled, int defaultDuration, double defaultRatio,
                               int maxConcurrent, boolean autoStop, int errorThreshold) {
        this.abTestEnabled = enabled;
        this.defaultTestDurationMinutes = defaultDuration;
        this.defaultTrafficRatio = defaultRatio;
        this.maxConcurrentTests = maxConcurrent;
        this.autoStopOnError = autoStop;
        this.errorThreshold = errorThreshold;
        
        logger.info("A/B测试配置已更新: 启用={}, 默认持续时间={}, 默认比例={}, 最大并发={}, 自动停止={}, 错误阈值={}", 
                   enabled, defaultDuration, defaultRatio, maxConcurrent, autoStop, errorThreshold);
    }

    /**
     * 执行A/B测试逻辑
     */
    private ScriptVersionSelection executeABTestLogic(ABTestSession testSession, String requestId) {
        try {
            totalABTestRequests.incrementAndGet();
            
            // 使用请求ID的哈希值进行流量分配
            int hash = Math.abs(requestId.hashCode());
            double randomValue = (hash % 100) / 100.0;
            
            String selectedVersion;
            boolean isTestVersion;
            
            if (randomValue < testSession.getTrafficRatio()) {
                selectedVersion = testSession.getVersionA();
                totalVersionARequests.incrementAndGet();
                isTestVersion = true;
            } else {
                selectedVersion = testSession.getVersionB();
                totalVersionBRequests.incrementAndGet();
                isTestVersion = true;
            }
            
            // 获取选中的脚本版本
            ScriptVersionManager.ScriptVersion scriptVersion = 
                scriptVersionManager.getScriptVersion(testSession.getScriptName(), selectedVersion);
            
            return new ScriptVersionSelection(scriptVersion, selectedVersion, isTestVersion);
            
        } catch (Exception e) {
            logger.error("执行A/B测试逻辑失败", e);
            totalABTestErrors.incrementAndGet();
            
            // 返回活跃版本作为降级
            ScriptVersionManager.ScriptVersion activeVersion = 
                scriptVersionManager.getActiveScriptVersion(testSession.getScriptName());
            return new ScriptVersionSelection(activeVersion, "fallback", true);
        }
    }

    /**
     * 生成测试结果
     */
    private ABTestResult generateTestResult(ABTestSession testSession, String winningVersion) {
        return new ABTestResult(
            testSession.getScriptName(),
            testSession.getVersionA(),
            testSession.getVersionB(),
            testSession.getStartTime(),
            System.currentTimeMillis(),
            testSession.getVersionARequests(),
            testSession.getVersionBRequests(),
            testSession.getVersionASuccess(),
            testSession.getVersionBSuccess(),
            testSession.getVersionAErrors(),
            testSession.getVersionBErrors(),
            testSession.getTotalVersionAProcessingTime(),
            testSession.getTotalVersionBProcessingTime(),
            winningVersion
        );
    }

    /**
     * 启动A/B测试监控任务
     */
    private void startABTestMonitoringTask() {
        scheduler.scheduleAtFixedRate(this::monitorABTests, 
            0, 30, TimeUnit.SECONDS);
    }

    /**
     * 监控A/B测试
     */
    private void monitorABTests() {
        try {
            long currentTime = System.currentTimeMillis();
            List<String> expiredTests = new ArrayList<>();
            
            for (Map.Entry<String, ABTestSession> entry : activeTests.entrySet()) {
                ABTestSession testSession = entry.getValue();
                long elapsedTime = currentTime - testSession.getStartTime();
                long durationMs = testSession.getDuration() * 60 * 1000;
                
                if (elapsedTime > durationMs) {
                    expiredTests.add(entry.getKey());
                }
            }
            
            for (String scriptName : expiredTests) {
                logger.info("A/B测试已过期，自动停止: {}", scriptName);
                stopABTest(scriptName, null);
            }
            
        } catch (Exception e) {
            logger.error("监控A/B测试失败", e);
        }
    }

    /**
     * A/B测试会话类
     */
    public static class ABTestSession {
        private final String scriptName;
        private final String versionA;
        private final String versionB;
        private final double trafficRatio;
        private final int duration;
        private final long startTime;
        
        private long versionARequests;
        private long versionBRequests;
        private long versionASuccess;
        private long versionBSuccess;
        private long versionAErrors;
        private long versionBErrors;
        private long totalVersionAProcessingTime;
        private long totalVersionBProcessingTime;

        public ABTestSession(String scriptName, String versionA, String versionB,
                           double trafficRatio, int duration, long startTime) {
            this.scriptName = scriptName;
            this.versionA = versionA;
            this.versionB = versionB;
            this.trafficRatio = trafficRatio;
            this.duration = duration;
            this.startTime = startTime;
        }

        // Getter和Setter方法
        public String getScriptName() { return scriptName; }
        public String getVersionA() { return versionA; }
        public String getVersionB() { return versionB; }
        public double getTrafficRatio() { return trafficRatio; }
        public int getDuration() { return duration; }
        public long getStartTime() { return startTime; }
        public long getVersionARequests() { return versionARequests; }
        public void incrementVersionARequests() { this.versionARequests++; }
        public long getVersionBRequests() { return versionBRequests; }
        public void incrementVersionBRequests() { this.versionBRequests++; }
        public long getVersionASuccess() { return versionASuccess; }
        public void incrementVersionASuccess() { this.versionASuccess++; }
        public long getVersionBSuccess() { return versionBSuccess; }
        public void incrementVersionBSuccess() { this.versionBSuccess++; }
        public long getVersionAErrors() { return versionAErrors; }
        public void incrementVersionAErrors() { this.versionAErrors++; }
        public long getVersionBErrors() { return versionBErrors; }
        public void incrementVersionBErrors() { this.versionBErrors++; }
        public long getTotalVersionAProcessingTime() { return totalVersionAProcessingTime; }
        public void addVersionAProcessingTime(long processingTime) { this.totalVersionAProcessingTime += processingTime; }
        public long getTotalVersionBProcessingTime() { return totalVersionBProcessingTime; }
        public void addVersionBProcessingTime(long processingTime) { this.totalVersionBProcessingTime += processingTime; }
        
        public long getTotalRequests() { return versionARequests + versionBRequests; }
        public long getTotalSuccess() { return versionASuccess + versionBSuccess; }
        public long getTotalErrors() { return versionAErrors + versionBErrors; }
        
        public double getVersionASuccessRate() {
            return versionARequests > 0 ? (double) versionASuccess / versionARequests : 0.0;
        }
        
        public double getVersionBSuccessRate() {
            return versionBRequests > 0 ? (double) versionBSuccess / versionBRequests : 0.0;
        }
        
        public double getVersionAAverageProcessingTime() {
            return versionARequests > 0 ? (double) totalVersionAProcessingTime / versionARequests : 0.0;
        }
        
        public double getVersionBAverageProcessingTime() {
            return versionBRequests > 0 ? (double) totalVersionBProcessingTime / versionBRequests : 0.0;
        }
    }

    /**
     * 脚本版本选择结果类
     */
    public static class ScriptVersionSelection {
        private final ScriptVersionManager.ScriptVersion scriptVersion;
        private final String version;
        private final boolean isTestVersion;

        public ScriptVersionSelection(ScriptVersionManager.ScriptVersion scriptVersion, 
                                    String version, boolean isTestVersion) {
            this.scriptVersion = scriptVersion;
            this.version = version;
            this.isTestVersion = isTestVersion;
        }

        // Getter方法
        public ScriptVersionManager.ScriptVersion getScriptVersion() { return scriptVersion; }
        public String getVersion() { return version; }
        public boolean isTestVersion() { return isTestVersion; }
    }

    /**
     * A/B测试结果类
     */
    public static class ABTestResult {
        private final String scriptName;
        private final String versionA;
        private final String versionB;
        private final long startTime;
        private final long endTime;
        private final long versionARequests;
        private final long versionBRequests;
        private final long versionASuccess;
        private final long versionBSuccess;
        private final long versionAErrors;
        private final long versionBErrors;
        private final long totalVersionAProcessingTime;
        private final long totalVersionBProcessingTime;
        private final String winningVersion;

        public ABTestResult(String scriptName, String versionA, String versionB,
                          long startTime, long endTime, long versionARequests, long versionBRequests,
                          long versionASuccess, long versionBSuccess, long versionAErrors, long versionBErrors,
                          long totalVersionAProcessingTime, long totalVersionBProcessingTime, String winningVersion) {
            this.scriptName = scriptName;
            this.versionA = versionA;
            this.versionB = versionB;
            this.startTime = startTime;
            this.endTime = endTime;
            this.versionARequests = versionARequests;
            this.versionBRequests = versionBRequests;
            this.versionASuccess = versionASuccess;
            this.versionBSuccess = versionBSuccess;
            this.versionAErrors = versionAErrors;
            this.versionBErrors = versionBErrors;
            this.totalVersionAProcessingTime = totalVersionAProcessingTime;
            this.totalVersionBProcessingTime = totalVersionBProcessingTime;
            this.winningVersion = winningVersion;
        }

        // Getter方法
        public String getScriptName() { return scriptName; }
        public String getVersionA() { return versionA; }
        public String getVersionB() { return versionB; }
        public long getStartTime() { return startTime; }
        public long getEndTime() { return endTime; }
        public long getVersionARequests() { return versionARequests; }
        public long getVersionBRequests() { return versionBRequests; }
        public long getVersionASuccess() { return versionASuccess; }
        public long getVersionBSuccess() { return versionBSuccess; }
        public long getVersionAErrors() { return versionAErrors; }
        public long getVersionBErrors() { return versionBErrors; }
        public long getTotalVersionAProcessingTime() { return totalVersionAProcessingTime; }
        public long getTotalVersionBProcessingTime() { return totalVersionBProcessingTime; }
        public String getWinningVersion() { return winningVersion; }
        
        public long getTotalRequests() { return versionARequests + versionBRequests; }
        public long getTotalSuccess() { return versionASuccess + versionBSuccess; }
        public long getTotalErrors() { return versionAErrors + versionBErrors; }
        
        public double getVersionASuccessRate() {
            return versionARequests > 0 ? (double) versionASuccess / versionARequests : 0.0;
        }
        
        public double getVersionBSuccessRate() {
            return versionBRequests > 0 ? (double) versionBSuccess / versionBRequests : 0.0;
        }
        
        public double getVersionAAverageProcessingTime() {
            return versionARequests > 0 ? (double) totalVersionAProcessingTime / versionARequests : 0.0;
        }
        
        public double getVersionBAverageProcessingTime() {
            return versionBRequests > 0 ? (double) totalVersionBProcessingTime / versionBRequests : 0.0;
        }
        
        public long getDuration() { return endTime - startTime; }
    }

    /**
     * A/B测试统计信息类
     */
    public static class ABTestStatistics {
        private final long totalABTestRequests;
        private final long totalVersionARequests;
        private final long totalVersionBRequests;
        private final long totalABTestErrors;
        private final int activeTests;
        private final int completedTests;
        private final boolean abTestEnabled;
        private final int defaultTestDuration;
        private final double defaultTrafficRatio;
        private final int maxConcurrentTests;
        private final long timestamp;

        public ABTestStatistics(long totalABTestRequests, long totalVersionARequests, long totalVersionBRequests,
                              long totalABTestErrors, int activeTests, int completedTests,
                              boolean abTestEnabled, int defaultTestDuration, double defaultTrafficRatio,
                              int maxConcurrentTests, long timestamp) {
            this.totalABTestRequests = totalABTestRequests;
            this.totalVersionARequests = totalVersionARequests;
            this.totalVersionBRequests = totalVersionBRequests;
            this.totalABTestErrors = totalABTestErrors;
            this.activeTests = activeTests;
            this.completedTests = completedTests;
            this.abTestEnabled = abTestEnabled;
            this.defaultTestDuration = defaultTestDuration;
            this.defaultTrafficRatio = defaultTrafficRatio;
            this.maxConcurrentTests = maxConcurrentTests;
            this.timestamp = timestamp;
        }

        // Getter方法
        public long getTotalABTestRequests() { return totalABTestRequests; }
        public long getTotalVersionARequests() { return totalVersionARequests; }
        public long getTotalVersionBRequests() { return totalVersionBRequests; }
        public long getTotalABTestErrors() { return totalABTestErrors; }
        public int getActiveTests() { return activeTests; }
        public int getCompletedTests() { return completedTests; }
        public boolean isAbTestEnabled() { return abTestEnabled; }
        public int getDefaultTestDuration() { return defaultTestDuration; }
        public double getDefaultTrafficRatio() { return defaultTrafficRatio; }
        public int getMaxConcurrentTests() { return maxConcurrentTests; }
        public long getTimestamp() { return timestamp; }
    }
}
